Prerrequisitos

Se requieren cargar las siguientes paqueterías para seguir los ejemplos.

library(easypackages)
libraries("tidyverse","fpp3","plotly", "patchwork")

Introducción

Los modelos que veremos aquí tienen como idea principal encontrar relaciones lineales entre la serie que queremos pronosticar, \(y\), con una o más series distintas, x. En otras palabras, pronosticaremos los valores futuros de una serie, a partir de los cambios en otra serie que la afecte.

Es muy común querer predecir de esta forma. Por ejemplo, una tienda de helados podría encontrar una relación entre sus ventas (\(y\)) y la temperatura (\(x_1\)). O las ventas de Nike, a partir de cuánto gastan en publicidad y mercadotecnia.

En la literatura podemos encontrar muchos nombres para las variables \(y\) ^ \(x\). P. ej.

\(y\) (var. de pronóstico) \(x\) (vars. predictoras)
Var. dependiente Vars. independientes
Explicada Explicativas
Regresada Regresoras
Respuesta Estímulo
Resultado Covariante
Controlada De control

El modelo lineal

El caso más sencillo sería un modelo de regresión lineal simple, de la forma:

\[ y_t = \beta_0 + \beta_1 x_t + \varepsilon_t \]

donde

  • \(\beta_0\) es conocido como el intercepto y representa el valor predicho cuando \(x = 0\).

  • \(\beta_1\) es la pendiente de la recta. Nos indica el cambio promedio en \(y\), ante un cambio en una unidad de \(x\).

  • El término de error, \(\varepsilon_t\) se asume aleatorio y decimos que captura los cambios debido a todas las otras variables que pudieran llegar a afectar a \(y_t\), que no están explícitamente especificadas en el modelo.

La recta resultante está dada entonces por \(\beta_0 + \beta_1 x_t\), y la diferencia que existe en los puntos reales y ésta es \(\varepsilon_t\).

Ejemplo: gasto de consumo en EEUU

Como primer ejemplo, veamos las tasas de crecimiento del gasto de consumo, \(y\), y su relación con el ingreso personal disponible, \(x\).

La gráfica de tiempo de ambas series:

us_change %>%
  ggplot(aes(x = Quarter)) +
  geom_line(aes(y = Consumption, colour = "Consumo")) +
  geom_line(aes(y = Income, colour = "Ingreso")) +
  ylab("cambio %") + xlab("Año") +
  guides(colour=guide_legend(title="Series")) + 
  theme(legend.position = "top")

Un diagrama de dispersión entre ambas series, para ver una posible correlación.

us_change %>%
  ggplot(aes(x=Income, y=Consumption)) +
    ylab("Consumo (cambio % trimestral)") +
    xlab("Ingreso (cambio % trimestral)") +
    geom_point() +
    geom_smooth(method="lm", se=FALSE)

La línea azul en el gráfico sigue la ecuación que describe la regresión lineal que tiene por variable dependiente al consumo y como independiente al ingreso.

¿Qué es una regresión lineal?

El término de regresión fue acuñado por primera vez por Francis Galton en 1886. Él estaba estudiando la relación que existe entre la estatura de los hijos y la estatura de los padres.

Lo que encontró fue lo siguiente, en resumen:

  • Los padres más altos, tendían a tener hijos más altos, mientras que los padres bajos tendían a tener hijos bajos.

  • En promedio, los hijos de padres altos no logran ser más altos que ellos. Similarmente, los hijos de padres bajos, en promedio son más altos que sus papás.

  • Así, Galton decía que había una tendencia a regresar a la estatura promedio.

De no cumplirse la regresión de Galton, sería común tener gente de la estatura de un Hobbit, y también de la estatura de un gigante.

Entonces, el análisis de regresión en tiempos modernos trata sobre la relación de la dependencia entre una variable \(y\), respecto de una o más variables exógenas (regresoras \(x\)) para predecir el valor promedio de la variable dependiente.

Regresión y causalidad

“Una relación estadística, por más fuerte y sugerente que sea, nunca podrá establecer una conexión causal: nuestras ideas de causalidad deben provenir de estadísticas externas y, en último término, de una u otra teoría” (Kendall & Stuart, 1961)

Regresión \(\neq\) Causalidad

Correlación \(\neq\) Causalidad



  • Una relación estadística por sí misma no puede implicar causalidad.
    • Se debe acudir a consideraciones a priori o teóricas.


  • La causalidad puede determinarse también por sentido común.

¿Qué significa que un modelo sea lineal?

Se puede hablar de linealidad en dos sentidos:

  1. Linealidad en las variables; \(x\).
  • La esperanza condicional de \(y\) es una función lineal de \(x_i\).
  1. Linealidad en los parámetros; \(\beta\).
  • La esperanza condicional de \(y\) es lineal en los parámetros \(\beta_i\).

Para un modelo de regresión lineal, solo nos interesa que sea lineal en los parámetros.

Todas estas funciones son lineales en los parámetros y pueden ser estimadas mediante un modelo de regresión lineal

Así, un modelo de regresión lineal puede generar una recta, o una variedad de curvas, dependiendo la forma funcional que se elija.

Mínimos cuadrados ordinarios (MCO-OLS)

\[ H_0: \beta_0 = 0 \\ H_1: \beta_0 \neq 0 \] \[ H_0: \beta_1 = 0 \\ H_1: \beta_1 \neq 0 \]

\[ \hat{y}_t = \hat{\beta}_0 + \hat{\beta}_1x_t + \hat{\varepsilon}_t \\ \hat{y}_t = 0.54454 + 0.27183 x_t + \hat{\varepsilon}_t \]

\[ y_{consumo} = \beta_0 + \beta_1 x_{income} + \beta_2 x_{production} + \beta_3 x_{savings} + \beta_4 x_{unemployment} \]

Ejemplos

US % change

us_change
us_change %>% 
  as_tibble() %>% 
  select(-Quarter) %>% 
  GGally::ggpairs()

Una correlación, por más fuerte que sea entre dos variables, no puede implicar por sí misma causalidad.

us_change %>% 
  pivot_longer(cols = -Quarter) %>% 
  ggplot(aes(x = Quarter, y = value, color = name)) +
  geom_line() +
  facet_wrap(~ name, scales = "free_y") +
  theme(legend.position = "none")

Graficando nuestra variable de pronóstico (Consumo) vs. cada una de las variables predictoras:

us_change %>% 
  pivot_longer(cols = -c(Quarter, Consumption)) %>% 
  ggplot(aes(x = Quarter, y = value, color = name)) +
  geom_line() +
  geom_line(aes(y = Consumption), color = "black") +
  facet_wrap(~ name, scales = "free_y") +
  theme(legend.position = "none")

Regresión lineal simple

Realizamos un primer modelo, donde utilizaremos de variable predictora al ingreso disponible, para pronosticar el consumo.

\[ y_{t,Consumo} = \beta_0 + \beta_{1}x_{t,Ingreso} + \varepsilon_t \]

fit1 <- us_change %>% 
  model(reg_lin_simple = TSLM(Consumption ~ Income)
        )
fit1 %>%  report()
Series: Consumption 
Model: TSLM 

Residuals:
     Min       1Q   Median       3Q      Max 
-2.58236 -0.27777  0.01862  0.32330  1.42229 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  0.54454    0.05403  10.079  < 2e-16 ***
Income       0.27183    0.04673   5.817  2.4e-08 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.5905 on 196 degrees of freedom
Multiple R-squared: 0.1472, Adjusted R-squared: 0.1429
F-statistic: 33.84 on 1 and 196 DF, p-value: 2.4022e-08

Prueba de significancia individual para cada uno de los parámetros (\(\beta_i\))

\[ H_0: \beta_i = 0 \]

Prueba de significancia conjunta (para revisar si nuestro modelo sirve o no)

\[ H_0: \beta_1 = \beta_2 = \beta_3 = \ldots = 0 \]

augment(fit1) %>% 
  ggplot(aes(x = Quarter)) +
  geom_line(aes(y = Consumption, color = "Datos")) +
  geom_line(aes(y = .fitted, color = "Fitted"))+
  xlab("Año") + ylab(NULL) +
  ggtitle("Cambios porcentuales en el gasto de Consumo en EEUU") +
  guides(color = guide_legend(title = NULL))

El modelo no parece capturar adecuadamente la variación de los datos reales.

augment(fit1) %>% 
  ggplot(aes(x = Consumption, y = .fitted)) +
  geom_point() +
  ylab("Fitted (valores ajustados)") +
  xlab("Datos (reales históricos)") +
  ggtitle("Cambios porcentuales en el gasto de Consumo en EEUU") +
  geom_abline(intercept = 0, slope = 1)

fit1 %>% 
  gg_tsresiduals()

augment(fit1) %>% 
  features(.resid, ljung_box, lag= 10, dof = 2)

Es evidente que este modelo se puede mejorar. Probemos incluyendo las otras predictoras.

Regresión lineal múltiple

Podríamos proponer ahora un modelo en donde el consumo sea una función del ingreso, la producción, los ahorros y el desempleo:

\[ y_{t,Consumo} = \beta_0 + \beta_{1}x_{t,Ingreso} + \beta_2x_{t,Producción} + \beta_3x_{t,Ahorros} + \beta_4x_{t,Desempleo} + \varepsilon_t \]

fit2 <- us_change %>% 
  model(
    reg_lin_multiple = TSLM(Consumption ~ Income + Production + Savings + Unemployment)
  )
report(fit2)
Series: Consumption 
Model: TSLM 

Residuals:
     Min       1Q   Median       3Q      Max 
-0.90555 -0.15821 -0.03608  0.13618  1.15471 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)   0.253105   0.034470   7.343 5.71e-12 ***
Income        0.740583   0.040115  18.461  < 2e-16 ***
Production    0.047173   0.023142   2.038   0.0429 *  
Savings      -0.052890   0.002924 -18.088  < 2e-16 ***
Unemployment -0.174685   0.095511  -1.829   0.0689 .  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.3102 on 193 degrees of freedom
Multiple R-squared: 0.7683, Adjusted R-squared: 0.7635
F-statistic:   160 on 4 and 193 DF, p-value: < 2.22e-16
augment(fit2) %>% 
  ggplot(aes(x = Quarter)) +
  geom_line(aes(y = Consumption, color = "Datos")) +
  geom_line(aes(y = .fitted, color = "Fitted"))+
  xlab("Año") + ylab(NULL) +
  ggtitle("Cambios porcentuales en el gasto de Consumo en EEUU") +
  guides(color = guide_legend(title = NULL))

Este modelo parece capturar más variación de los datos históricos.

augment(fit2) %>% 
  ggplot(aes(x = Consumption, y = .fitted)) +
  geom_point() +
  ylab("Fitted (valores ajustados)") +
  xlab("Datos (reales históricos)") +
  ggtitle("Cambios porcentuales en el gasto de Consumo en EEUU") +
  geom_abline(intercept = 0, slope = 1)

fit2 %>% 
  gg_tsresiduals()

augment(fit2) %>% 
  features(.resid, ljung_box, lag= 10, dof = 2)
df <- left_join(us_change, residuals(fit2), by = "Quarter")
df %>% 
  select(-c(Consumption, .model)) %>% 
  pivot_longer(cols = c(Income:Unemployment)) %>% 
  ggplot(aes( x = value, y = .resid, color = name)) + 
  geom_point() + ylab("Residuales") + xlab("Predictoras") +
  facet_wrap(~ name, scales = "free_x") +
  theme(legend.position = "none")

augment(fit2) %>% 
  ggplot(aes(x = .fitted, y = .resid)) +
  geom_point() +
  labs(x = "Ajustados", y = "Residuales")

glance(fit2) %>% 
  select(adj_r_squared, AIC, AICc, BIC)

Selección de predictoras

  1. Escoger subconjuntos de predictoras y probarlo.
fit3 <- us_change %>% 
  model(r1 = TSLM(Consumption ~ Income),
        r2 = TSLM(Consumption ~ Income + Production),
        r3 = TSLM(Consumption ~ Income + Production + Savings + Unemployment),
        r4 = TSLM(Consumption ~ Income + Production + Savings),
        r5 = TSLM(Consumption ~ Income + Savings + Unemployment),
        r6 = TSLM(Consumption ~ Income + Production + Unemployment),
        r7 = TSLM(Consumption ~ Income + Savings)
        )
fit3 %>% 
  glance() %>% 
  select(.model, adj_r_squared, AIC, AICc, BIC)
fit3 %>% 
  select(r3) %>% 
  report()
Series: Consumption 
Model: TSLM 

Residuals:
     Min       1Q   Median       3Q      Max 
-0.90555 -0.15821 -0.03608  0.13618  1.15471 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)   0.253105   0.034470   7.343 5.71e-12 ***
Income        0.740583   0.040115  18.461  < 2e-16 ***
Production    0.047173   0.023142   2.038   0.0429 *  
Savings      -0.052890   0.002924 -18.088  < 2e-16 ***
Unemployment -0.174685   0.095511  -1.829   0.0689 .  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.3102 on 193 degrees of freedom
Multiple R-squared: 0.7683, Adjusted R-squared: 0.7635
F-statistic:   160 on 4 and 193 DF, p-value: < 2.22e-16
  1. Backwards stepwise regression:
  • Empezamos con un modelo que contenga todas las predictoras.
  • Quitamos una a la vez.
  • Mantenemos el modelo si mejora la medida de desempeño predictivo (\(\bar{R}^2\), AICc,…).
  • Seguirlo haciendo hasta no encontrar mejoras adicionales.

Selección del modelo a través de backwards stepwise regression con base en el \(AICc\) sugiere conservar el modelo que incluye todas las predictoras.

us_change %>% 
  model(i = TSLM(Consumption ~ Income + Production + Savings + Unemployment),
        ii = TSLM(Consumption ~ Income + Production + Savings),
        iii = TSLM(Consumption ~ Income + Production + Unemployment),
        iv = TSLM(Consumption ~ Income + Savings + Unemployment),
        v = TSLM(Consumption ~ Production + Savings + Unemployment)
        ) %>% 
  glance() %>% 
  select(.model, adj_r_squared, AIC, AICc, BIC)

Con base en \(BIC\) parece que el mejor modelo es el que incluye todas las predictoras menos el desempleo.

us_change %>% 
  model(i = TSLM(Consumption ~ Income + Production + Savings + Unemployment),
        ii = TSLM(Consumption ~ Income + Production + Savings),
        iii = TSLM(Consumption ~ Income + Production),
        iv = TSLM(Consumption ~ Income + Savings),
        v = TSLM(Consumption ~ Production + Savings),
        vi = TSLM(Consumption ~ Income + Savings + Unemployment),
        vii = TSLM(Consumption ~ Production + Savings + Unemployment),
        viii = TSLM(Consumption ~ Income + Production + Unemployment)
        ) %>% 
  glance() %>% 
  select(.model, adj_r_squared, AIC, AICc, BIC)
  1. Forwards stepwise regression:
  • Comenzar con un modelo que solo incluya al intercepto.
  • Se van agregando las predictoras una a la vez.
  • La predictora que mejore más al modelo se mantiene.
  • Se itera hasta no tener mejoría adicional.

Pronóstico

Pronósticos ex-ante

En estos pronósticos solo se utiliza información disponible hasta el último dato del histórico. A estos pronósticos se les considera como pronósticos reales. Aquí las predictoras se deben pronosticar antes de poder producir el pronóstico de la variable de interés.

Pronósticos ex-post

Con estos pronósticos se utiliza información real disponible de las predictoras. Estos pronósticos ya no son reales (en el sentido estricto). La variable a pronosticar (\(y\)) sigue siendo desconocida.

Pronósticos basados en escenarios

fit_escenarios <- us_change %>% 
  model(lineal = TSLM(Consumption ~ Income + Savings + Unemployment))
# Necesitamos agregar nuevos datos de las predictoras
optimista_futuro <- new_data(us_change,4) %>% 
  mutate(Income = 0.5, 
         Savings = 0.5, 
         Unemployment = 0)

pesimista_futuro <- new_data(us_change,4) %>% 
  mutate(Income = -1, 
         Savings = -0.5, 
         Unemployment = 0)

fc_optimista <- forecast(fit_escenarios, new_data = optimista_futuro) %>% 
  mutate(Escenario = "Optimista") #%>% # REVISAR 
  # as_fable(response = "Consumo", distribution = Consumption, key = c("Escenario",".model"))

fc_pesimista <- forecast(fit_escenarios, new_data = pesimista_futuro) %>% 
  mutate(Escenario = "Pesimista") # %>% # REVISAR 
  # as_fable(response = "Consumo", distribution = Consumption, key = c("Escenario",".model"))

# bind_rows(fc_optimista,fc_pesimista)

# us_change %>% 
#   autoplot(Consumption) +
#   autolayer(bind_rows(fc_optimista,fc_pesimista))

p1 <- us_change %>% 
  autoplot(Consumption) +
  autolayer(fc_optimista) +
  ggtitle("Escenario optimista")
  
p2 <- us_change %>% 
  autoplot(Consumption) +
  autolayer(fc_pesimista) +
  ggtitle("Escenario pesimista")

p1 / p2

Vamos a generar un pronóstico ex-ante del consumo. Para esto, necesitamos primero producir pronósticos de las predictoras:

# mod_predictoras <- function(predictora, horizonte = 4) {
#   us_change %>% 
#     model(predictora = ARIMA(as.formula(predictora)) %>% 
#     forecast(h = horizonte)
# }
# 
# mod_predictoras(predictora = Income)

ingreso <-  us_change %>% 
  model(ETS = ETS(Income),
        ARIMA = ARIMA(Income)
        ) %>% 
  forecast(h = 4) 

ingreso %>% 
  autoplot(us_change, level = NULL)


produccion <- us_change %>% 
  model(
    ETS = ETS(Production),
    ARIMA = ARIMA(Production)
  ) %>% 
  forecast(h = 4)
produccion %>% 
  autoplot(us_change, level = NULL)


ahorro <- us_change %>% 
  model(
    ETS = ETS(Savings),
    ARIMA = ARIMA(Savings)
  ) %>% 
  forecast(h = 4)
ahorro %>% 
  autoplot(us_change, level = NULL)


desempleo <- us_change %>% 
  model(
    ETS = ETS(Unemployment),
    ARIMA = ARIMA(Unemployment)
  ) %>% 
  forecast(h = 4)
desempleo %>% 
  autoplot(us_change, level = NULL)

Teniendo ya los pronósticos de cada predictora, podemos proceder a generar el pronóstico del consumo.

fit <- us_change %>% 
  model(
    `Regresión lineal múltiple` = TSLM(Consumption ~ Income + Production + Savings + Unemployment)
  )

datos_futuros <- new_data(us_change,4) %>% 
  mutate(Income = ingreso %>% filter(.model == "ARIMA") %>% pull(.mean), 
         Savings = ahorro %>% filter(.model == "ARIMA") %>% pull(.mean), 
         Unemployment = desempleo %>% filter(.model == "ARIMA") %>% pull(.mean),
         Production = produccion %>% filter(.model == "ARIMA") %>% pull(.mean))

datos_futuros

fc <- forecast(fit, datos_futuros)

fc %>% 
  autoplot(us_change)

fc %>% 
  autoplot()

Inclusión de predictoras útiles: Producción de cerveza

recent_production <- aus_production %>% 
  filter(year(Quarter) >= 1992)

recent_production

recent_production %>% 
  autoplot(Beer) +
  labs(x = "Año", y = "Megalitros", 
       title = "Producción de cerveza trimestral en Australia")

Existen varias predictoras que pueden ser útiles en el análisis de regresión.

  1. Predictora de tendencia trend()

\[ y_t = \beta_0 + \beta_1t + \varepsilon_t \]

  1. Variables dummy estacionales

Las variables dummy (también llamadas dicotómicas, binarias, …) toman solo dos valores: 1 o 0 (verdadero/falso).

Para agregar variables dummy estacionales, basta con escribir season().

recent_production %>% 
  select(Quarter,Beer) %>% 
  mutate(tendencia = seq_along(recent_production$Quarter),
         q2 = if_else(quarter(Quarter)==2,1,0),
         q3 = if_else(quarter(Quarter)==3,1,0),
         q4 = if_else(quarter(Quarter)==4,1,0)
         ) %>% 
  model(TSLM(Beer ~ tendencia + q2 + q3 + q4)) %>% 
  report()
Series: Beer 
Model: TSLM 

Residuals:
     Min       1Q   Median       3Q      Max 
-42.9029  -7.5995  -0.4594   7.9908  21.7895 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 441.80044    3.73353 118.333  < 2e-16 ***
tendencia    -0.34027    0.06657  -5.111 2.73e-06 ***
q2          -34.65973    3.96832  -8.734 9.10e-13 ***
q3          -17.82164    4.02249  -4.430 3.45e-05 ***
q4           72.79641    4.02305  18.095  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 12.23 on 69 degrees of freedom
Multiple R-squared: 0.9243, Adjusted R-squared: 0.9199
F-statistic: 210.7 on 4 and 69 DF, p-value: < 2.22e-16
fit_beer <- recent_production %>% 
  model(TSLM(Beer ~ trend() + season()))

report(fit_beer)
Series: Beer 
Model: TSLM 

Residuals:
     Min       1Q   Median       3Q      Max 
-42.9029  -7.5995  -0.4594   7.9908  21.7895 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)   441.80044    3.73353 118.333  < 2e-16 ***
trend()        -0.34027    0.06657  -5.111 2.73e-06 ***
season()year2 -34.65973    3.96832  -8.734 9.10e-13 ***
season()year3 -17.82164    4.02249  -4.430 3.45e-05 ***
season()year4  72.79641    4.02305  18.095  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 12.23 on 69 degrees of freedom
Multiple R-squared: 0.9243, Adjusted R-squared: 0.9199
F-statistic: 210.7 on 4 and 69 DF, p-value: < 2.22e-16
p <- augment(fit_beer) %>% 
  ggplot(aes(x = Quarter)) +
  geom_line(aes(y = Beer, color = "Datos")) +
  geom_line(aes(y = .fitted, color = "Fitted")) +
  labs(x = "Año", y = "Megalitros")

ggplotly(p)

Al ver la gráfica, vemos que existe un trimestre muy por debajo del resto. Podemos agregar una variable de intervención para ese periodo.

Spike variables

Capturan el efecto de un solo periodo.

cerveza <- recent_production %>% 
  select(Quarter, Beer) %>% 
  mutate(q2_94 = if_else(Quarter == yearquarter("1994 Q2"),1,0),
         q4_92 = if_else(Quarter == yearquarter("1992 Q4"),1,0))
cerveza

Ajustamos un modelo, corrigiendo por ese periodo outlier (1994 Q2).

fit_beer <- cerveza %>% 
  model(TSLM(Beer ~ trend() + season() + q2_94))

report(fit_beer)
Series: Beer 
Model: TSLM 

Residuals:
     Min       1Q   Median       3Q      Max 
-42.6171  -6.9644  -0.8231   8.1306  21.7895 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)   442.55585    3.67748 120.342  < 2e-16 ***
trend()        -0.36068    0.06603  -5.462 7.20e-07 ***
season()year2 -33.34433    3.94421  -8.454 3.28e-12 ***
season()year3 -17.82164    3.94060  -4.523 2.51e-05 ***
season()year4  72.81682    3.94115  18.476  < 2e-16 ***
q2_94         -24.60467   12.46254  -1.974   0.0524 .  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 11.98 on 68 degrees of freedom
Multiple R-squared: 0.9284, Adjusted R-squared: 0.9232
F-statistic: 176.4 on 5 and 68 DF, p-value: < 2.22e-16
p <- augment(fit_beer) %>% 
  ggplot(aes(x = Quarter)) +
  geom_line(aes(y = Beer, color = "Datos")) +
  geom_line(aes(y = .fitted, color = "Fitted")) +
  labs(x = "Año", y = "Megalitros")

ggplotly(p)

Cambios de nivel

Capturan el efecto a partir de cierto periodo.

cerveza <- cerveza %>% 
  mutate(d2000 = if_else(year(Quarter)>=2000,1,0))
cerveza
fit_beer <- cerveza %>% 
  model(TSLM(Beer ~ trend() + season() + d2000))

report(fit_beer)
Series: Beer 
Model: TSLM 

Residuals:
     Min       1Q   Median       3Q      Max 
-43.0235  -7.5945  -0.4539   7.7754  21.4829 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)   441.9154     3.8644 114.354  < 2e-16 ***
trend()        -0.3548     0.1308  -2.712  0.00846 ** 
season()year2 -34.6452     3.9985  -8.665 1.36e-12 ***
season()year3 -17.8046     4.0536  -4.392 4.02e-05 ***
season()year4  72.8279     4.0594  17.941  < 2e-16 ***
d2000           0.7282     5.6402   0.129  0.89766    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 12.32 on 68 degrees of freedom
Multiple R-squared: 0.9243, Adjusted R-squared: 0.9188
F-statistic: 166.1 on 5 and 68 DF, p-value: < 2.22e-16
p <- augment(fit_beer) %>% 
  ggplot(aes(x = Quarter)) +
  geom_line(aes(y = Beer, color = "Datos")) +
  geom_line(aes(y = .fitted, color = "Fitted")) +
  labs(x = "Año", y = "Megalitros")

ggplotly(p)
cerveza %>% 
  gg_tsdisplay(Beer %>% difference(4), plot_type = "partial")

tibble(mujer = c(1,0,1, 0),
       hombre = c(0,1,0, 0),
       nombre = c("Andrea","Juan","Sofía","Fer"))

Cuando se crean varaibles dummy, la cantidad de dummies a generar es una menos que el total de categorías. Si son dos categorías, solo necesitamos una dummy. Si son tres, necesitamos 2, etc.

Regresiones no lineales

Modelos exponenciales

  • Modelos log-log

\[ \log{y_t} = \beta_0 + \beta_1 \log{x_t} \]

La interpretación de los coeficientes (\(\beta_s\)) es como elasticidades (cambios porcentuales).

  • Modelos lin-log

\[ y_t = \beta_0 + \beta_1 \log{x_t} \]

  • Modelos log-lin

\[ \log{y_t} = \beta_0 + \beta_1 x_t \]

Modelos de regresión lineal por partes (piecewise)

Aquí la regresión se calcula por partes en el tiempo, para capturar distintas tendencias.

boston_men <- boston_marathon %>% 
  filter(Event == "Men's open division") %>% 
  mutate(Minutes = as.numeric(Time)/60)

boston_men %>% 
  autoplot(Minutes) + 
  ggtitle("Tiempos ganadores del maratón de Boston, categoría abierta de hombres")

Vamos a modelar los tiempos con una regresión por partes.

fit_boston <- boston_men %>% 
  model(
    lineal = TSLM(Minutes ~ trend()),
    exponencial = TSLM(log(Minutes) ~ trend()),
    `Reg. por partes` = TSLM(Minutes ~ trend(knots = c(1940,1980)))
  )

fc_boston <- fit_boston %>% forecast(h = 10)

boston_men %>% 
  autoplot(Minutes) +
  geom_line(aes(y = .fitted, color = .model), data = fitted(fit_boston)) +
  autolayer(fc_boston, alpha = 0.5, level = 95) +
  ggtitle("Maratón de Boston, cat. abierta de hombres")

LS0tDQp0aXRsZTogIk1vZGVsb3MgZGUgcmVncmVzacOzbiBwYXJhIHNlcmllcyBkZSB0aWVtcG8iDQphdXRob3I6ICJQYWJsbyBCZW5hdmlkZXMtSGVycmVyYSINCmRhdGU6IDIwMjAtMTItMDYNCm91dHB1dDogDQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiBUUlVFDQogICAgdG9jX2Zsb2F0OiBUUlVFDQogICAgdGhlbWU6IHVuaXRlZA0KICAgIGhpZ2hsaWdodDogdGFuZ28NCi0tLQ0KDQojIFByZXJyZXF1aXNpdG9zDQoNClNlIHJlcXVpZXJlbiBjYXJnYXIgbGFzIHNpZ3VpZW50ZXMgcGFxdWV0ZXLDrWFzIHBhcmEgc2VndWlyIGxvcyBlamVtcGxvcy4NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KGVhc3lwYWNrYWdlcykNCmxpYnJhcmllcygidGlkeXZlcnNlIiwiZnBwMyIsInBsb3RseSIsICJwYXRjaHdvcmsiKQ0KYGBgDQoNCg0KIyBJbnRyb2R1Y2Npw7NuDQoNCkxvcyBtb2RlbG9zIHF1ZSB2ZXJlbW9zIGFxdcOtIHRpZW5lbiBjb21vIGlkZWEgcHJpbmNpcGFsIGVuY29udHJhciByZWxhY2lvbmVzIGxpbmVhbGVzIGVudHJlIGxhIHNlcmllIHF1ZSBxdWVyZW1vcyBwcm9ub3N0aWNhciwgJHkkLCBjb24gdW5hIG8gbcOhcyBzZXJpZXMgZGlzdGludGFzLCAqeCouIEVuIG90cmFzIHBhbGFicmFzLCBwcm9ub3N0aWNhcmVtb3MgbG9zIHZhbG9yZXMgZnV0dXJvcyBkZSB1bmEgc2VyaWUsIGEgcGFydGlyIGRlIGxvcyBjYW1iaW9zIGVuIG90cmEgc2VyaWUgcXVlIGxhIGFmZWN0ZS4NCg0KRXMgbXV5IGNvbcO6biBxdWVyZXIgcHJlZGVjaXIgZGUgZXN0YSBmb3JtYS4gUG9yIGVqZW1wbG8sIHVuYSB0aWVuZGEgZGUgaGVsYWRvcyBwb2Ryw61hIGVuY29udHJhciB1bmEgcmVsYWNpw7NuIGVudHJlIHN1cyAqKnZlbnRhcyoqICgkeSQpIHkgbGEgKip0ZW1wZXJhdHVyYSoqICgkeF8xJCkuIE8gbGFzICoqdmVudGFzKiogZGUgKk5pa2UqLCBhIHBhcnRpciBkZSBjdcOhbnRvICoqZ2FzdGFuIGVuIHB1YmxpY2lkYWQgeSBtZXJjYWRvdGVjbmlhKiouDQoNCkVuIGxhIGxpdGVyYXR1cmEgcG9kZW1vcyBlbmNvbnRyYXIgbXVjaG9zIG5vbWJyZXMgcGFyYSBsYXMgdmFyaWFibGVzICR5JCBeICR4JC4gUC4gZWouDQoNCnwgJHkkICh2YXIuIGRlIHByb27Ds3N0aWNvKSB8ICR4JCAodmFycy4gcHJlZGljdG9yYXMpIHwNCnw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tOnw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS06fA0KfCBWYXIuIGRlcGVuZGllbnRlICAgICAgICAgfCAgVmFycy4gaW5kZXBlbmRpZW50ZXMgICB8DQp8IEV4cGxpY2FkYSAgICAgICAgICAgICAgICB8ICBFeHBsaWNhdGl2YXMgICAgICAgICAgIHwNCnwgUmVncmVzYWRhICAgICAgICAgICAgICAgIHwgIFJlZ3Jlc29yYXMgICAgICAgICAgICAgfA0KfCBSZXNwdWVzdGEgICAgICAgICAgICAgICAgfCAgRXN0w61tdWxvICAgICAgICAgICAgICAgfA0KfCBSZXN1bHRhZG8gICAgICAgICAgICAgICAgfCAgQ292YXJpYW50ZSAgICAgICAgICAgICB8DQp8IENvbnRyb2xhZGEgICAgICAgICAgICAgICB8ICBEZSBjb250cm9sICAgICAgICAgICAgIHwNCg0KDQojIEVsIG1vZGVsbyBsaW5lYWwNCg0KRWwgY2FzbyBtw6FzIHNlbmNpbGxvIHNlcsOtYSB1biAqKm1vZGVsbyBkZSByZWdyZXNpw7NuIGxpbmVhbCBzaW1wbGUqKiwgZGUgbGEgZm9ybWE6DQoNCiQkDQp5X3QgPSBcYmV0YV8wICsgXGJldGFfMSB4X3QgKyBcdmFyZXBzaWxvbl90DQokJA0KDQpkb25kZSANCg0KKiAkXGJldGFfMCQgZXMgY29ub2NpZG8gY29tbyBlbCAqaW50ZXJjZXB0byogeSByZXByZXNlbnRhIGVsICoqdmFsb3IgcHJlZGljaG8gY3VhbmRvICR4ID0gMCQuKiogDQoNCiogJFxiZXRhXzEkIGVzIGxhICpwZW5kaWVudGUqIGRlIGxhIHJlY3RhLiBOb3MgaW5kaWNhIGVsICoqY2FtYmlvIHByb21lZGlvIGVuICR5JCwgYW50ZSB1biBjYW1iaW8gZW4gdW5hIHVuaWRhZCBkZSAkeCQqKi4NCg0KKiBFbCB0w6lybWlubyBkZSBlcnJvciwgJFx2YXJlcHNpbG9uX3QkIHNlIGFzdW1lIGFsZWF0b3JpbyB5IGRlY2ltb3MgcXVlIGNhcHR1cmEgbG9zIGNhbWJpb3MgZGViaWRvIGEgdG9kYXMgbGFzIG90cmFzIHZhcmlhYmxlcyBxdWUgcHVkaWVyYW4gbGxlZ2FyIGEgYWZlY3RhciBhICR5X3QkLCBxdWUgbm8gZXN0w6FuIGV4cGzDrWNpdGFtZW50ZSBlc3BlY2lmaWNhZGFzIGVuIGVsIG1vZGVsby4NCg0KTGEgcmVjdGEgcmVzdWx0YW50ZSBlc3TDoSBkYWRhIGVudG9uY2VzIHBvciAkXGJldGFfMCArIFxiZXRhXzEgeF90JCwgeSBsYSBkaWZlcmVuY2lhIHF1ZSBleGlzdGUgZW4gbG9zIHB1bnRvcyByZWFsZXMgeSDDqXN0YSBlcyAkXHZhcmVwc2lsb25fdCQuDQoNCg0KIVsqWyhIeW5kbWFuLCAyMDE5KV0oaHR0cHM6Ly9vdGV4dHMuY29tL2ZwcDMvKSpdKC4uL2ltYWdlcy9saW5yZWcucG5nKQ0KDQojIyBFamVtcGxvOiBnYXN0byBkZSBjb25zdW1vIGVuIEVFVVUNCg0KQ29tbyBwcmltZXIgZWplbXBsbywgdmVhbW9zIGxhcyB0YXNhcyBkZSBjcmVjaW1pZW50byBkZWwgZ2FzdG8gZGUgY29uc3VtbywgJHkkLCB5IHN1IHJlbGFjacOzbiBjb24gZWwgaW5ncmVzbyBwZXJzb25hbCBkaXNwb25pYmxlLCAkeCQuDQoNCkxhIGdyw6FmaWNhIGRlIHRpZW1wbyBkZSBhbWJhcyBzZXJpZXM6DQoNCmBgYHtyfQ0KdXNfY2hhbmdlICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBRdWFydGVyKSkgKw0KICBnZW9tX2xpbmUoYWVzKHkgPSBDb25zdW1wdGlvbiwgY29sb3VyID0gIkNvbnN1bW8iKSkgKw0KICBnZW9tX2xpbmUoYWVzKHkgPSBJbmNvbWUsIGNvbG91ciA9ICJJbmdyZXNvIikpICsNCiAgeWxhYigiY2FtYmlvICUiKSArIHhsYWIoIkHDsW8iKSArDQogIGd1aWRlcyhjb2xvdXI9Z3VpZGVfbGVnZW5kKHRpdGxlPSJTZXJpZXMiKSkgKyANCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpDQpgYGANCg0KVW4gZGlhZ3JhbWEgZGUgZGlzcGVyc2nDs24gZW50cmUgYW1iYXMgc2VyaWVzLCBwYXJhIHZlciB1bmEgcG9zaWJsZSBjb3JyZWxhY2nDs24uDQoNCmBgYHtyfQ0KdXNfY2hhbmdlICU+JQ0KICBnZ3Bsb3QoYWVzKHg9SW5jb21lLCB5PUNvbnN1bXB0aW9uKSkgKw0KICAgIHlsYWIoIkNvbnN1bW8gKGNhbWJpbyAlIHRyaW1lc3RyYWwpIikgKw0KICAgIHhsYWIoIkluZ3Jlc28gKGNhbWJpbyAlIHRyaW1lc3RyYWwpIikgKw0KICAgIGdlb21fcG9pbnQoKSArDQogICAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIHNlPUZBTFNFKQ0KYGBgDQoNCkxhIGzDrW5lYSBhenVsIGVuIGVsIGdyw6FmaWNvIHNpZ3VlIGxhIGVjdWFjacOzbiBxdWUgZGVzY3JpYmUgbGEgKipyZWdyZXNpw7NuIGxpbmVhbCoqIHF1ZSB0aWVuZSBwb3IgdmFyaWFibGUgZGVwZW5kaWVudGUgYWwgY29uc3VtbyB5IGNvbW8gaW5kZXBlbmRpZW50ZSBhbCBpbmdyZXNvLg0KDQojIyDCv1F1w6kgZXMgdW5hIHJlZ3Jlc2nDs24gbGluZWFsPw0KDQojIyMgDQoNCiFbXSguLi9pbWFnZXMvZWRfZWRkX2VkZGllLmdpZikNCg0KRWwgdMOpcm1pbm8gZGUgcmVncmVzacOzbiBmdWUgYWN1w7FhZG8gcG9yIHByaW1lcmEgdmV6IHBvciBGcmFuY2lzIEdhbHRvbiBlbiAxODg2LiDDiWwgZXN0YWJhIGVzdHVkaWFuZG8gbGEgcmVsYWNpw7NuIHF1ZSBleGlzdGUgZW50cmUgbGEgZXN0YXR1cmEgZGUgbG9zIGhpam9zIHkgbGEgZXN0YXR1cmEgZGUgbG9zIHBhZHJlcy4NCg0KTG8gcXVlIGVuY29udHLDsyBmdWUgbG8gc2lndWllbnRlLCBlbiByZXN1bWVuOg0KDQoqIExvcyBwYWRyZXMgbcOhcyBhbHRvcywgdGVuZMOtYW4gYSB0ZW5lciBoaWpvcyBtw6FzIGFsdG9zLCBtaWVudHJhcyBxdWUgbG9zIHBhZHJlcyBiYWpvcyB0ZW5kw61hbiBhIHRlbmVyIGhpam9zIGJham9zLg0KDQoqIEVuIHByb21lZGlvLCBsb3MgaGlqb3MgZGUgcGFkcmVzIGFsdG9zIG5vIGxvZ3JhbiBzZXIgbcOhcyBhbHRvcyBxdWUgZWxsb3MuIFNpbWlsYXJtZW50ZSwgbG9zIGhpam9zIGRlIHBhZHJlcyBiYWpvcywgZW4gcHJvbWVkaW8gc29uIG3DoXMgYWx0b3MgcXVlIHN1cyBwYXDDoXMuDQoNCiogQXPDrSwgR2FsdG9uIGRlY8OtYSBxdWUgaGFiw61hIHVuYSB0ZW5kZW5jaWEgYSAqKnJlZ3Jlc2FyKiogYSBsYSBlc3RhdHVyYSBwcm9tZWRpby4NCg0KDQoNCiFbXShpbWcvcmVncmVzc2lvbi5wbmcpDQoNCiFbKkRlIG5vIGN1bXBsaXJzZSBsYSByZWdyZXNpw7NuIGRlIEdhbHRvbiwgc2Vyw61hIGNvbcO6biB0ZW5lciBnZW50ZSBkZSBsYSBlc3RhdHVyYSBkZSB1biBIb2JiaXQsIHkgdGFtYmnDqW4gZGUgbGEgZXN0YXR1cmEgZGUgdW4gZ2lnYW50ZS4qXShpbWcvZmVsbG93c2hpcC5qcGcpDQoNCkVudG9uY2VzLCAqZWwgYW7DoWxpc2lzIGRlIHJlZ3Jlc2nDs24gZW4gdGllbXBvcyBtb2Rlcm5vcyB0cmF0YSBzb2JyZSBsYSByZWxhY2nDs24gZGUgbGEgZGVwZW5kZW5jaWEgZW50cmUgdW5hIHZhcmlhYmxlICR5JCwgcmVzcGVjdG8gZGUgdW5hIG8gbcOhcyB2YXJpYWJsZXMgZXjDs2dlbmFzIChyZWdyZXNvcmFzICR4JCkgcGFyYSBwcmVkZWNpciBlbCB2YWxvciBwcm9tZWRpbyBkZSBsYSB2YXJpYWJsZSBkZXBlbmRpZW50ZS4qDQoNCiMjIFJlZ3Jlc2nDs24geSBjYXVzYWxpZGFkDQoNCjxzdHlsZT4NCmRpdi5vcmNoaWQgeyBiYWNrZ3JvdW5kLWNvbG9yOiBvcmNoaWQ7IGJvcmRlci1yYWRpdXM6IDVweDsgcGFkZGluZzogMjBweDsgY29sb3I6IGJsYWNrfQ0KPC9zdHlsZT4NCjxkaXYgY2xhc3MgPSAib3JjaGlkIj4NCg0KPuKAnFVuYSByZWxhY2nDs24gZXN0YWTDrXN0aWNhLCBwb3IgbcOhcyBmdWVydGUgeSBzdWdlcmVudGUgcXVlIHNlYSwgbnVuY2EgcG9kcsOhIGVzdGFibGVjZXIgdW5hIGNvbmV4acOzbiBjYXVzYWw6IG51ZXN0cmFzIGlkZWFzIGRlIGNhdXNhbGlkYWQgZGViZW4gcHJvdmVuaXIgZGUgZXN0YWTDrXN0aWNhcyBleHRlcm5hcyB5LCBlbiDDumx0aW1vIHTDqXJtaW5vLCBkZSB1bmEgdSBvdHJhIHRlb3LDrWHigJ0gKEtlbmRhbGwgJiBTdHVhcnQsIDE5NjEpDQoNCjxwIHN0eWxlPSJ0ZXh0LWFsaWduOmNlbnRlcjtmb250LXdlaWdodDpib2xkIj4gDQpSZWdyZXNpw7NuICRcbmVxJCBDYXVzYWxpZGFkDQo8L3A+DQo8cCBzdHlsZT0idGV4dC1hbGlnbjpjZW50ZXI7Zm9udC13ZWlnaHQ6Ym9sZCI+IA0KQ29ycmVsYWNpw7NuICRcbmVxJCBDYXVzYWxpZGFkDQo8L3A+DQoNCjwvZGl2Pg0KDQo8YnI+DQo8YnI+DQoNCiogVW5hIHJlbGFjacOzbiBlc3RhZMOtc3RpY2EgcG9yIHPDrSBtaXNtYSBubyBwdWVkZSBpbXBsaWNhciBjYXVzYWxpZGFkLg0KICAqIFNlIGRlYmUgYWN1ZGlyIGEgY29uc2lkZXJhY2lvbmVzIGEgcHJpb3JpIG8gdGXDs3JpY2FzLg0KDQo8YnI+DQoNCiogTGEgY2F1c2FsaWRhZCBwdWVkZSBkZXRlcm1pbmFyc2UgdGFtYmnDqW4gcG9yIHNlbnRpZG8gY29tw7puLg0KDQojIyDCv1F1w6kgc2lnbmlmaWNhIHF1ZSB1biBtb2RlbG8gc2VhIGxpbmVhbD8NCg0KU2UgcHVlZGUgaGFibGFyIGRlIGxpbmVhbGlkYWQgZW4gZG9zIHNlbnRpZG9zOg0KDQoxLiBMaW5lYWxpZGFkIGVuIGxhcyB2YXJpYWJsZXM7ICR4JC4NCiAgLSBMYSBlc3BlcmFuemEgY29uZGljaW9uYWwgZGUgJHkkIGVzIHVuYSBmdW5jacOzbiBsaW5lYWwgZGUgJHhfaSQuDQogIA0KMi4gTGluZWFsaWRhZCBlbiBsb3MgcGFyw6FtZXRyb3M7ICRcYmV0YSQuDQogIC0gTGEgZXNwZXJhbnphIGNvbmRpY2lvbmFsIGRlICR5JCBlcyBsaW5lYWwgZW4gbG9zIHBhcsOhbWV0cm9zICRcYmV0YV9pJC4NCg0KUGFyYSB1biBtb2RlbG8gZGUgcmVncmVzacOzbiBsaW5lYWwsIHNvbG8gbm9zIGludGVyZXNhIHF1ZSBzZWEgKipsaW5lYWwgZW4gbG9zIHBhcsOhbWV0cm9zKiouDQoNCiFbKlRvZGFzIGVzdGFzIGZ1bmNpb25lcyBzb24gKipsaW5lYWxlcyBlbiBsb3MgcGFyw6FtZXRyb3MqKiB5IHB1ZWRlbiBzZXIgZXN0aW1hZGFzIG1lZGlhbnRlIHVuIG1vZGVsbyBkZSByZWdyZXNpw7NuIGxpbmVhbCpdKGltZy9saW5lYXIucG5nKQ0KDQpBc8OtLCB1biBtb2RlbG8gZGUgcmVncmVzacOzbiBsaW5lYWwgcHVlZGUgZ2VuZXJhciB1bmEgcmVjdGEsIG8gdW5hIHZhcmllZGFkIGRlIGN1cnZhcywgZGVwZW5kaWVuZG8gbGEgKipmb3JtYSBmdW5jaW9uYWwqKiBxdWUgc2UgZWxpamEuDQoNCiMjIE3DrW5pbW9zIGN1YWRyYWRvcyBvcmRpbmFyaW9zIChNQ08tT0xTKQ0KDQoNCg0KJCQNCkhfMDogXGJldGFfMCA9IDAgXFwNCkhfMTogXGJldGFfMCBcbmVxIDANCiQkDQokJA0KSF8wOiBcYmV0YV8xID0gMCBcXA0KSF8xOiBcYmV0YV8xIFxuZXEgMA0KJCQNCg0KDQokJA0KXGhhdHt5fV90ID0gXGhhdHtcYmV0YX1fMCArIFxoYXR7XGJldGF9XzF4X3QgKyBcaGF0e1x2YXJlcHNpbG9ufV90IFxcDQpcaGF0e3l9X3QgPSAwLjU0NDU0ICsgMC4yNzE4MyB4X3QgKyBcaGF0e1x2YXJlcHNpbG9ufV90DQokJA0KDQokJA0KeV97Y29uc3Vtb30gPSBcYmV0YV8wICsgXGJldGFfMSB4X3tpbmNvbWV9ICsgXGJldGFfMiB4X3twcm9kdWN0aW9ufSArIFxiZXRhXzMgeF97c2F2aW5nc30gKyBcYmV0YV80IHhfe3VuZW1wbG95bWVudH0NCiQkDQoNCg0KIyBFamVtcGxvcw0KDQojIyBVUyAlIGNoYW5nZQ0KDQpgYGB7cn0NCnVzX2NoYW5nZQ0KYGBgDQoNCmBgYHtyfQ0KdXNfY2hhbmdlICU+JSANCiAgYXNfdGliYmxlKCkgJT4lIA0KICBzZWxlY3QoLVF1YXJ0ZXIpICU+JSANCiAgR0dhbGx5OjpnZ3BhaXJzKCkNCmBgYA0KDQoqKlVuYSBjb3JyZWxhY2nDs24sIHBvciBtw6FzIGZ1ZXJ0ZSBxdWUgc2VhIGVudHJlIGRvcyB2YXJpYWJsZXMsIG5vIHB1ZWRlIGltcGxpY2FyIHBvciBzw60gbWlzbWEgY2F1c2FsaWRhZC4qKg0KDQpgYGB7cn0NCnVzX2NoYW5nZSAlPiUgDQogIHBpdm90X2xvbmdlcihjb2xzID0gLVF1YXJ0ZXIpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gUXVhcnRlciwgeSA9IHZhbHVlLCBjb2xvciA9IG5hbWUpKSArDQogIGdlb21fbGluZSgpICsNCiAgZmFjZXRfd3JhcCh+IG5hbWUsIHNjYWxlcyA9ICJmcmVlX3kiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQpHcmFmaWNhbmRvIG51ZXN0cmEgdmFyaWFibGUgZGUgcHJvbsOzc3RpY28gKENvbnN1bW8pIHZzLiBjYWRhIHVuYSBkZSBsYXMgdmFyaWFibGVzIHByZWRpY3RvcmFzOg0KDQpgYGB7cn0NCnVzX2NoYW5nZSAlPiUgDQogIHBpdm90X2xvbmdlcihjb2xzID0gLWMoUXVhcnRlciwgQ29uc3VtcHRpb24pKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IFF1YXJ0ZXIsIHkgPSB2YWx1ZSwgY29sb3IgPSBuYW1lKSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGdlb21fbGluZShhZXMoeSA9IENvbnN1bXB0aW9uKSwgY29sb3IgPSAiYmxhY2siKSArDQogIGZhY2V0X3dyYXAofiBuYW1lLCBzY2FsZXMgPSAiZnJlZV95IikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpgYGANCg0KDQojIyMgUmVncmVzacOzbiBsaW5lYWwgc2ltcGxlDQoNClJlYWxpemFtb3MgdW4gcHJpbWVyIG1vZGVsbywgZG9uZGUgdXRpbGl6YXJlbW9zIGRlIHZhcmlhYmxlIHByZWRpY3RvcmEgYWwgaW5ncmVzbyBkaXNwb25pYmxlLCBwYXJhIHByb25vc3RpY2FyIGVsIGNvbnN1bW8uDQoNCiQkDQp5X3t0LENvbnN1bW99ID0gXGJldGFfMCArIFxiZXRhX3sxfXhfe3QsSW5ncmVzb30gKyBcdmFyZXBzaWxvbl90DQokJA0KDQpgYGB7cn0NCmZpdDEgPC0gdXNfY2hhbmdlICU+JSANCiAgbW9kZWwocmVnX2xpbl9zaW1wbGUgPSBUU0xNKENvbnN1bXB0aW9uIH4gSW5jb21lKQ0KICAgICAgICApDQpmaXQxICU+JSAgcmVwb3J0KCkNCmBgYA0KDQpQcnVlYmEgZGUgc2lnbmlmaWNhbmNpYSBpbmRpdmlkdWFsIHBhcmEgY2FkYSB1bm8gZGUgbG9zIHBhcsOhbWV0cm9zICgkXGJldGFfaSQpDQoNCiQkIA0KSF8wOiBcYmV0YV9pID0gMA0KJCQNCg0KUHJ1ZWJhIGRlIHNpZ25pZmljYW5jaWEgY29uanVudGEgKHBhcmEgcmV2aXNhciBzaSBudWVzdHJvIG1vZGVsbyBzaXJ2ZSBvIG5vKQ0KDQokJA0KSF8wOiBcYmV0YV8xID0gXGJldGFfMiA9IFxiZXRhXzMgPSBcbGRvdHMgPSAwDQokJA0KDQoNCmBgYHtyfQ0KYXVnbWVudChmaXQxKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IFF1YXJ0ZXIpKSArDQogIGdlb21fbGluZShhZXMoeSA9IENvbnN1bXB0aW9uLCBjb2xvciA9ICJEYXRvcyIpKSArDQogIGdlb21fbGluZShhZXMoeSA9IC5maXR0ZWQsIGNvbG9yID0gIkZpdHRlZCIpKSsNCiAgeGxhYigiQcOxbyIpICsgeWxhYihOVUxMKSArDQogIGdndGl0bGUoIkNhbWJpb3MgcG9yY2VudHVhbGVzIGVuIGVsIGdhc3RvIGRlIENvbnN1bW8gZW4gRUVVVSIpICsNCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gTlVMTCkpDQpgYGANCg0KRWwgbW9kZWxvIG5vIHBhcmVjZSBjYXB0dXJhciBhZGVjdWFkYW1lbnRlIGxhIHZhcmlhY2nDs24gZGUgbG9zIGRhdG9zIHJlYWxlcy4NCg0KYGBge3J9DQphdWdtZW50KGZpdDEpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gQ29uc3VtcHRpb24sIHkgPSAuZml0dGVkKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICB5bGFiKCJGaXR0ZWQgKHZhbG9yZXMgYWp1c3RhZG9zKSIpICsNCiAgeGxhYigiRGF0b3MgKHJlYWxlcyBoaXN0w7NyaWNvcykiKSArDQogIGdndGl0bGUoIkNhbWJpb3MgcG9yY2VudHVhbGVzIGVuIGVsIGdhc3RvIGRlIENvbnN1bW8gZW4gRUVVVSIpICsNCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxKQ0KYGBgDQoNCmBgYHtyfQ0KZml0MSAlPiUgDQogIGdnX3RzcmVzaWR1YWxzKCkNCmBgYA0KDQpgYGB7cn0NCmF1Z21lbnQoZml0MSkgJT4lIA0KICBmZWF0dXJlcygucmVzaWQsIGxqdW5nX2JveCwgbGFnPSAxMCwgZG9mID0gMikNCmBgYA0KRXMgZXZpZGVudGUgcXVlIGVzdGUgbW9kZWxvIHNlIHB1ZWRlIG1lam9yYXIuIFByb2JlbW9zIGluY2x1eWVuZG8gbGFzIG90cmFzIHByZWRpY3RvcmFzLg0KDQojIyBSZWdyZXNpw7NuIGxpbmVhbCBtw7psdGlwbGUNCg0KUG9kcsOtYW1vcyBwcm9wb25lciBhaG9yYSB1biBtb2RlbG8gZW4gZG9uZGUgZWwgY29uc3VtbyBzZWEgdW5hIGZ1bmNpw7NuIGRlbCBpbmdyZXNvLCBsYSBwcm9kdWNjacOzbiwgbG9zIGFob3Jyb3MgeSBlbCBkZXNlbXBsZW86DQoNCiQkDQp5X3t0LENvbnN1bW99ID0gXGJldGFfMCArIFxiZXRhX3sxfXhfe3QsSW5ncmVzb30gKyBcYmV0YV8yeF97dCxQcm9kdWNjacOzbn0gKyBcYmV0YV8zeF97dCxBaG9ycm9zfSArIFxiZXRhXzR4X3t0LERlc2VtcGxlb30gKyBcdmFyZXBzaWxvbl90DQokJA0KDQpgYGB7cn0NCmZpdDIgPC0gdXNfY2hhbmdlICU+JSANCiAgbW9kZWwoDQogICAgcmVnX2xpbl9tdWx0aXBsZSA9IFRTTE0oQ29uc3VtcHRpb24gfiBJbmNvbWUgKyBQcm9kdWN0aW9uICsgU2F2aW5ncyArIFVuZW1wbG95bWVudCkNCiAgKQ0KcmVwb3J0KGZpdDIpDQpgYGANCg0KDQoNCmBgYHtyfQ0KYXVnbWVudChmaXQyKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IFF1YXJ0ZXIpKSArDQogIGdlb21fbGluZShhZXMoeSA9IENvbnN1bXB0aW9uLCBjb2xvciA9ICJEYXRvcyIpKSArDQogIGdlb21fbGluZShhZXMoeSA9IC5maXR0ZWQsIGNvbG9yID0gIkZpdHRlZCIpKSsNCiAgeGxhYigiQcOxbyIpICsgeWxhYihOVUxMKSArDQogIGdndGl0bGUoIkNhbWJpb3MgcG9yY2VudHVhbGVzIGVuIGVsIGdhc3RvIGRlIENvbnN1bW8gZW4gRUVVVSIpICsNCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gTlVMTCkpDQpgYGANCg0KRXN0ZSBtb2RlbG8gcGFyZWNlIGNhcHR1cmFyIG3DoXMgdmFyaWFjacOzbiBkZSBsb3MgZGF0b3MgaGlzdMOzcmljb3MuDQoNCmBgYHtyfQ0KYXVnbWVudChmaXQyKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IENvbnN1bXB0aW9uLCB5ID0gLmZpdHRlZCkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgeWxhYigiRml0dGVkICh2YWxvcmVzIGFqdXN0YWRvcykiKSArDQogIHhsYWIoIkRhdG9zIChyZWFsZXMgaGlzdMOzcmljb3MpIikgKw0KICBnZ3RpdGxlKCJDYW1iaW9zIHBvcmNlbnR1YWxlcyBlbiBlbCBnYXN0byBkZSBDb25zdW1vIGVuIEVFVVUiKSArDQogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMSkNCmBgYA0KDQpgYGB7cn0NCmZpdDIgJT4lIA0KICBnZ190c3Jlc2lkdWFscygpDQpgYGANCg0KYGBge3J9DQphdWdtZW50KGZpdDIpICU+JSANCiAgZmVhdHVyZXMoLnJlc2lkLCBsanVuZ19ib3gsIGxhZz0gMTAsIGRvZiA9IDIpDQpgYGANCg0KDQpgYGB7cn0NCmRmIDwtIGxlZnRfam9pbih1c19jaGFuZ2UsIHJlc2lkdWFscyhmaXQyKSwgYnkgPSAiUXVhcnRlciIpDQpkZiAlPiUgDQogIHNlbGVjdCgtYyhDb25zdW1wdGlvbiwgLm1vZGVsKSkgJT4lIA0KICBwaXZvdF9sb25nZXIoY29scyA9IGMoSW5jb21lOlVuZW1wbG95bWVudCkpICU+JSANCiAgZ2dwbG90KGFlcyggeCA9IHZhbHVlLCB5ID0gLnJlc2lkLCBjb2xvciA9IG5hbWUpKSArIA0KICBnZW9tX3BvaW50KCkgKyB5bGFiKCJSZXNpZHVhbGVzIikgKyB4bGFiKCJQcmVkaWN0b3JhcyIpICsNCiAgZmFjZXRfd3JhcCh+IG5hbWUsIHNjYWxlcyA9ICJmcmVlX3giKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQpgYGB7cn0NCmF1Z21lbnQoZml0MikgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSAuZml0dGVkLCB5ID0gLnJlc2lkKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBsYWJzKHggPSAiQWp1c3RhZG9zIiwgeSA9ICJSZXNpZHVhbGVzIikNCmBgYA0KDQpgYGB7cn0NCmdsYW5jZShmaXQyKSAlPiUgDQogIHNlbGVjdChhZGpfcl9zcXVhcmVkLCBBSUMsIEFJQ2MsIEJJQykNCmBgYA0KDQoNCg0KIyMgU2VsZWNjacOzbiBkZSBwcmVkaWN0b3Jhcw0KDQoxLiBFc2NvZ2VyIHN1YmNvbmp1bnRvcyBkZSBwcmVkaWN0b3JhcyB5IHByb2JhcmxvLg0KDQpgYGB7cn0NCmZpdDMgPC0gdXNfY2hhbmdlICU+JSANCiAgbW9kZWwocjEgPSBUU0xNKENvbnN1bXB0aW9uIH4gSW5jb21lKSwNCiAgICAgICAgcjIgPSBUU0xNKENvbnN1bXB0aW9uIH4gSW5jb21lICsgUHJvZHVjdGlvbiksDQogICAgICAgIHIzID0gVFNMTShDb25zdW1wdGlvbiB+IEluY29tZSArIFByb2R1Y3Rpb24gKyBTYXZpbmdzICsgVW5lbXBsb3ltZW50KSwNCiAgICAgICAgcjQgPSBUU0xNKENvbnN1bXB0aW9uIH4gSW5jb21lICsgUHJvZHVjdGlvbiArIFNhdmluZ3MpLA0KICAgICAgICByNSA9IFRTTE0oQ29uc3VtcHRpb24gfiBJbmNvbWUgKyBTYXZpbmdzICsgVW5lbXBsb3ltZW50KSwNCiAgICAgICAgcjYgPSBUU0xNKENvbnN1bXB0aW9uIH4gSW5jb21lICsgUHJvZHVjdGlvbiArIFVuZW1wbG95bWVudCksDQogICAgICAgIHI3ID0gVFNMTShDb25zdW1wdGlvbiB+IEluY29tZSArIFNhdmluZ3MpDQogICAgICAgICkNCmZpdDMgJT4lIA0KICBnbGFuY2UoKSAlPiUgDQogIHNlbGVjdCgubW9kZWwsIGFkal9yX3NxdWFyZWQsIEFJQywgQUlDYywgQklDKQ0KYGBgDQoNCmBgYHtyfQ0KZml0MyAlPiUgDQogIHNlbGVjdChyMykgJT4lIA0KICByZXBvcnQoKQ0KYGBgDQoNCjIuIEJhY2t3YXJkcyBzdGVwd2lzZSByZWdyZXNzaW9uOg0KICAtIEVtcGV6YW1vcyBjb24gdW4gbW9kZWxvIHF1ZSBjb250ZW5nYSB0b2RhcyBsYXMgcHJlZGljdG9yYXMuDQogIC0gUXVpdGFtb3MgdW5hIGEgbGEgdmV6Lg0KICAtIE1hbnRlbmVtb3MgZWwgbW9kZWxvIHNpIG1lam9yYSBsYSBtZWRpZGEgZGUgZGVzZW1wZcOxbyBwcmVkaWN0aXZvICgkXGJhcntSfV4yJCwgQUlDYywuLi4pLg0KICAtIFNlZ3VpcmxvIGhhY2llbmRvIGhhc3RhIG5vIGVuY29udHJhciBtZWpvcmFzIGFkaWNpb25hbGVzLg0KDQpTZWxlY2Npw7NuIGRlbCBtb2RlbG8gYSB0cmF2w6lzIGRlICpiYWNrd2FyZHMgc3RlcHdpc2UgcmVncmVzc2lvbiogY29uIGJhc2UgZW4gZWwgJEFJQ2MkIHN1Z2llcmUgY29uc2VydmFyIGVsIG1vZGVsbyBxdWUgaW5jbHV5ZSAqKnRvZGFzIGxhcyBwcmVkaWN0b3JhcyoqLg0KDQpgYGB7cn0NCnVzX2NoYW5nZSAlPiUgDQogIG1vZGVsKGkgPSBUU0xNKENvbnN1bXB0aW9uIH4gSW5jb21lICsgUHJvZHVjdGlvbiArIFNhdmluZ3MgKyBVbmVtcGxveW1lbnQpLA0KICAgICAgICBpaSA9IFRTTE0oQ29uc3VtcHRpb24gfiBJbmNvbWUgKyBQcm9kdWN0aW9uICsgU2F2aW5ncyksDQogICAgICAgIGlpaSA9IFRTTE0oQ29uc3VtcHRpb24gfiBJbmNvbWUgKyBQcm9kdWN0aW9uICsgVW5lbXBsb3ltZW50KSwNCiAgICAgICAgaXYgPSBUU0xNKENvbnN1bXB0aW9uIH4gSW5jb21lICsgU2F2aW5ncyArIFVuZW1wbG95bWVudCksDQogICAgICAgIHYgPSBUU0xNKENvbnN1bXB0aW9uIH4gUHJvZHVjdGlvbiArIFNhdmluZ3MgKyBVbmVtcGxveW1lbnQpDQogICAgICAgICkgJT4lIA0KICBnbGFuY2UoKSAlPiUgDQogIHNlbGVjdCgubW9kZWwsIGFkal9yX3NxdWFyZWQsIEFJQywgQUlDYywgQklDKQ0KYGBgDQoNCkNvbiBiYXNlIGVuICRCSUMkIHBhcmVjZSBxdWUgZWwgbWVqb3IgbW9kZWxvIGVzIGVsIHF1ZSBpbmNsdXllIHRvZGFzIGxhcyBwcmVkaWN0b3JhcyAqKm1lbm9zIGVsIGRlc2VtcGxlbyoqLg0KDQpgYGB7cn0NCnVzX2NoYW5nZSAlPiUgDQogIG1vZGVsKGkgPSBUU0xNKENvbnN1bXB0aW9uIH4gSW5jb21lICsgUHJvZHVjdGlvbiArIFNhdmluZ3MgKyBVbmVtcGxveW1lbnQpLA0KICAgICAgICBpaSA9IFRTTE0oQ29uc3VtcHRpb24gfiBJbmNvbWUgKyBQcm9kdWN0aW9uICsgU2F2aW5ncyksDQogICAgICAgIGlpaSA9IFRTTE0oQ29uc3VtcHRpb24gfiBJbmNvbWUgKyBQcm9kdWN0aW9uKSwNCiAgICAgICAgaXYgPSBUU0xNKENvbnN1bXB0aW9uIH4gSW5jb21lICsgU2F2aW5ncyksDQogICAgICAgIHYgPSBUU0xNKENvbnN1bXB0aW9uIH4gUHJvZHVjdGlvbiArIFNhdmluZ3MpLA0KICAgICAgICB2aSA9IFRTTE0oQ29uc3VtcHRpb24gfiBJbmNvbWUgKyBTYXZpbmdzICsgVW5lbXBsb3ltZW50KSwNCiAgICAgICAgdmlpID0gVFNMTShDb25zdW1wdGlvbiB+IFByb2R1Y3Rpb24gKyBTYXZpbmdzICsgVW5lbXBsb3ltZW50KSwNCiAgICAgICAgdmlpaSA9IFRTTE0oQ29uc3VtcHRpb24gfiBJbmNvbWUgKyBQcm9kdWN0aW9uICsgVW5lbXBsb3ltZW50KQ0KICAgICAgICApICU+JSANCiAgZ2xhbmNlKCkgJT4lIA0KICBzZWxlY3QoLm1vZGVsLCBhZGpfcl9zcXVhcmVkLCBBSUMsIEFJQ2MsIEJJQykNCmBgYA0KDQoNCjMuIEZvcndhcmRzIHN0ZXB3aXNlIHJlZ3Jlc3Npb246DQogIC0gQ29tZW56YXIgY29uIHVuIG1vZGVsbyBxdWUgc29sbyBpbmNsdXlhIGFsIGludGVyY2VwdG8uDQogIC0gU2UgdmFuIGFncmVnYW5kbyBsYXMgcHJlZGljdG9yYXMgdW5hIGEgbGEgdmV6Lg0KICAtIExhIHByZWRpY3RvcmEgcXVlIG1lam9yZSBtw6FzIGFsIG1vZGVsbyBzZSBtYW50aWVuZS4NCiAgLSBTZSBpdGVyYSBoYXN0YSBubyB0ZW5lciBtZWpvcsOtYSBhZGljaW9uYWwuDQoNCiMjIFByb27Ds3N0aWNvDQoNCiMjIyBQcm9uw7NzdGljb3MgKmV4LWFudGUqDQoNCkVuIGVzdG9zIHByb27Ds3N0aWNvcyBzb2xvIHNlIHV0aWxpemEgaW5mb3JtYWNpw7NuIGRpc3BvbmlibGUgaGFzdGEgZWwgw7psdGltbyBkYXRvIGRlbCBoaXN0w7NyaWNvLiBBIGVzdG9zIHByb27Ds3N0aWNvcyBzZSBsZXMgY29uc2lkZXJhIGNvbW8gKipwcm9uw7NzdGljb3MgcmVhbGVzKiouIEFxdcOtIGxhcyBwcmVkaWN0b3JhcyBzZSBkZWJlbiBwcm9ub3N0aWNhciBhbnRlcyBkZSBwb2RlciBwcm9kdWNpciBlbCBwcm9uw7NzdGljbyBkZSBsYSB2YXJpYWJsZSBkZSBpbnRlcsOpcy4NCg0KIyMjIFByb27Ds3N0aWNvcyAqZXgtcG9zdCoNCg0KQ29uIGVzdG9zIHByb27Ds3N0aWNvcyBzZSB1dGlsaXphIGluZm9ybWFjacOzbiByZWFsIGRpc3BvbmlibGUgZGUgbGFzIHByZWRpY3RvcmFzLiBFc3RvcyBwcm9uw7NzdGljb3MgeWEgbm8gc29uICoqcmVhbGVzKiogKGVuIGVsIHNlbnRpZG8gZXN0cmljdG8pLiBMYSB2YXJpYWJsZSBhIHByb25vc3RpY2FyICgkeSQpIHNpZ3VlIHNpZW5kbyBkZXNjb25vY2lkYS4NCg0KIyMjIFByb27Ds3N0aWNvcyBiYXNhZG9zIGVuIGVzY2VuYXJpb3MNCg0KYGBge3IgcGFnZWQucHJpbnQ9RkFMU0V9DQpmaXRfZXNjZW5hcmlvcyA8LSB1c19jaGFuZ2UgJT4lIA0KICBtb2RlbChsaW5lYWwgPSBUU0xNKENvbnN1bXB0aW9uIH4gSW5jb21lICsgU2F2aW5ncyArIFVuZW1wbG95bWVudCkpDQojIE5lY2VzaXRhbW9zIGFncmVnYXIgbnVldm9zIGRhdG9zIGRlIGxhcyBwcmVkaWN0b3Jhcw0Kb3B0aW1pc3RhX2Z1dHVybyA8LSBuZXdfZGF0YSh1c19jaGFuZ2UsNCkgJT4lIA0KICBtdXRhdGUoSW5jb21lID0gMC41LCANCiAgICAgICAgIFNhdmluZ3MgPSAwLjUsIA0KICAgICAgICAgVW5lbXBsb3ltZW50ID0gMCkNCg0KcGVzaW1pc3RhX2Z1dHVybyA8LSBuZXdfZGF0YSh1c19jaGFuZ2UsNCkgJT4lIA0KICBtdXRhdGUoSW5jb21lID0gLTEsIA0KICAgICAgICAgU2F2aW5ncyA9IC0wLjUsIA0KICAgICAgICAgVW5lbXBsb3ltZW50ID0gMCkNCg0KZmNfb3B0aW1pc3RhIDwtIGZvcmVjYXN0KGZpdF9lc2NlbmFyaW9zLCBuZXdfZGF0YSA9IG9wdGltaXN0YV9mdXR1cm8pICU+JSANCiAgbXV0YXRlKEVzY2VuYXJpbyA9ICJPcHRpbWlzdGEiKSAjJT4lICMgUkVWSVNBUiANCiAgIyBhc19mYWJsZShyZXNwb25zZSA9ICJDb25zdW1vIiwgZGlzdHJpYnV0aW9uID0gQ29uc3VtcHRpb24sIGtleSA9IGMoIkVzY2VuYXJpbyIsIi5tb2RlbCIpKQ0KDQpmY19wZXNpbWlzdGEgPC0gZm9yZWNhc3QoZml0X2VzY2VuYXJpb3MsIG5ld19kYXRhID0gcGVzaW1pc3RhX2Z1dHVybykgJT4lIA0KICBtdXRhdGUoRXNjZW5hcmlvID0gIlBlc2ltaXN0YSIpICMgJT4lICMgUkVWSVNBUiANCiAgIyBhc19mYWJsZShyZXNwb25zZSA9ICJDb25zdW1vIiwgZGlzdHJpYnV0aW9uID0gQ29uc3VtcHRpb24sIGtleSA9IGMoIkVzY2VuYXJpbyIsIi5tb2RlbCIpKQ0KDQojIGJpbmRfcm93cyhmY19vcHRpbWlzdGEsZmNfcGVzaW1pc3RhKQ0KDQojIHVzX2NoYW5nZSAlPiUgDQojICAgYXV0b3Bsb3QoQ29uc3VtcHRpb24pICsNCiMgICBhdXRvbGF5ZXIoYmluZF9yb3dzKGZjX29wdGltaXN0YSxmY19wZXNpbWlzdGEpKQ0KDQpwMSA8LSB1c19jaGFuZ2UgJT4lIA0KICBhdXRvcGxvdChDb25zdW1wdGlvbikgKw0KICBhdXRvbGF5ZXIoZmNfb3B0aW1pc3RhKSArDQogIGdndGl0bGUoIkVzY2VuYXJpbyBvcHRpbWlzdGEiKQ0KICANCnAyIDwtIHVzX2NoYW5nZSAlPiUgDQogIGF1dG9wbG90KENvbnN1bXB0aW9uKSArDQogIGF1dG9sYXllcihmY19wZXNpbWlzdGEpICsNCiAgZ2d0aXRsZSgiRXNjZW5hcmlvIHBlc2ltaXN0YSIpDQoNCnAxIC8gcDINCmBgYA0KDQpWYW1vcyBhIGdlbmVyYXIgdW4gcHJvbsOzc3RpY28gKmV4LWFudGUqIGRlbCBjb25zdW1vLiBQYXJhIGVzdG8sIG5lY2VzaXRhbW9zIHByaW1lcm8gcHJvZHVjaXIgcHJvbsOzc3RpY29zIGRlIGxhcyBwcmVkaWN0b3JhczoNCg0KYGBge3J9DQojIG1vZF9wcmVkaWN0b3JhcyA8LSBmdW5jdGlvbihwcmVkaWN0b3JhLCBob3Jpem9udGUgPSA0KSB7DQojICAgdXNfY2hhbmdlICU+JSANCiMgICAgIG1vZGVsKHByZWRpY3RvcmEgPSBBUklNQShhcy5mb3JtdWxhKHByZWRpY3RvcmEpKSAlPiUgDQojICAgICBmb3JlY2FzdChoID0gaG9yaXpvbnRlKQ0KIyB9DQojIA0KIyBtb2RfcHJlZGljdG9yYXMocHJlZGljdG9yYSA9IEluY29tZSkNCg0KaW5ncmVzbyA8LSAgdXNfY2hhbmdlICU+JSANCiAgbW9kZWwoRVRTID0gRVRTKEluY29tZSksDQogICAgICAgIEFSSU1BID0gQVJJTUEoSW5jb21lKQ0KICAgICAgICApICU+JSANCiAgZm9yZWNhc3QoaCA9IDQpIA0KDQppbmdyZXNvICU+JSANCiAgYXV0b3Bsb3QodXNfY2hhbmdlLCBsZXZlbCA9IE5VTEwpDQoNCnByb2R1Y2Npb24gPC0gdXNfY2hhbmdlICU+JSANCiAgbW9kZWwoDQogICAgRVRTID0gRVRTKFByb2R1Y3Rpb24pLA0KICAgIEFSSU1BID0gQVJJTUEoUHJvZHVjdGlvbikNCiAgKSAlPiUgDQogIGZvcmVjYXN0KGggPSA0KQ0KcHJvZHVjY2lvbiAlPiUgDQogIGF1dG9wbG90KHVzX2NoYW5nZSwgbGV2ZWwgPSBOVUxMKQ0KDQphaG9ycm8gPC0gdXNfY2hhbmdlICU+JSANCiAgbW9kZWwoDQogICAgRVRTID0gRVRTKFNhdmluZ3MpLA0KICAgIEFSSU1BID0gQVJJTUEoU2F2aW5ncykNCiAgKSAlPiUgDQogIGZvcmVjYXN0KGggPSA0KQ0KYWhvcnJvICU+JSANCiAgYXV0b3Bsb3QodXNfY2hhbmdlLCBsZXZlbCA9IE5VTEwpDQoNCmRlc2VtcGxlbyA8LSB1c19jaGFuZ2UgJT4lIA0KICBtb2RlbCgNCiAgICBFVFMgPSBFVFMoVW5lbXBsb3ltZW50KSwNCiAgICBBUklNQSA9IEFSSU1BKFVuZW1wbG95bWVudCkNCiAgKSAlPiUgDQogIGZvcmVjYXN0KGggPSA0KQ0KZGVzZW1wbGVvICU+JSANCiAgYXV0b3Bsb3QodXNfY2hhbmdlLCBsZXZlbCA9IE5VTEwpDQpgYGANCg0KVGVuaWVuZG8geWEgbG9zIHByb27Ds3N0aWNvcyBkZSBjYWRhIHByZWRpY3RvcmEsIHBvZGVtb3MgcHJvY2VkZXIgYSBnZW5lcmFyIGVsIHByb27Ds3N0aWNvIGRlbCBjb25zdW1vLg0KDQpgYGB7cn0NCmZpdCA8LSB1c19jaGFuZ2UgJT4lIA0KICBtb2RlbCgNCiAgICBgUmVncmVzacOzbiBsaW5lYWwgbcO6bHRpcGxlYCA9IFRTTE0oQ29uc3VtcHRpb24gfiBJbmNvbWUgKyBQcm9kdWN0aW9uICsgU2F2aW5ncyArIFVuZW1wbG95bWVudCkNCiAgKQ0KDQpkYXRvc19mdXR1cm9zIDwtIG5ld19kYXRhKHVzX2NoYW5nZSw0KSAlPiUgDQogIG11dGF0ZShJbmNvbWUgPSBpbmdyZXNvICU+JSBmaWx0ZXIoLm1vZGVsID09ICJBUklNQSIpICU+JSBwdWxsKC5tZWFuKSwgDQogICAgICAgICBTYXZpbmdzID0gYWhvcnJvICU+JSBmaWx0ZXIoLm1vZGVsID09ICJBUklNQSIpICU+JSBwdWxsKC5tZWFuKSwgDQogICAgICAgICBVbmVtcGxveW1lbnQgPSBkZXNlbXBsZW8gJT4lIGZpbHRlcigubW9kZWwgPT0gIkFSSU1BIikgJT4lIHB1bGwoLm1lYW4pLA0KICAgICAgICAgUHJvZHVjdGlvbiA9IHByb2R1Y2Npb24gJT4lIGZpbHRlcigubW9kZWwgPT0gIkFSSU1BIikgJT4lIHB1bGwoLm1lYW4pKQ0KDQpkYXRvc19mdXR1cm9zDQoNCmZjIDwtIGZvcmVjYXN0KGZpdCwgZGF0b3NfZnV0dXJvcykNCg0KZmMgJT4lIA0KICBhdXRvcGxvdCh1c19jaGFuZ2UpDQpmYyAlPiUgDQogIGF1dG9wbG90KCkNCmBgYA0KDQoNCg0KIyMgSW5jbHVzacOzbiBkZSBwcmVkaWN0b3JhcyDDunRpbGVzOiBQcm9kdWNjacOzbiBkZSBjZXJ2ZXphDQoNCmBgYHtyfQ0KcmVjZW50X3Byb2R1Y3Rpb24gPC0gYXVzX3Byb2R1Y3Rpb24gJT4lIA0KICBmaWx0ZXIoeWVhcihRdWFydGVyKSA+PSAxOTkyKQ0KDQpyZWNlbnRfcHJvZHVjdGlvbg0KDQpyZWNlbnRfcHJvZHVjdGlvbiAlPiUgDQogIGF1dG9wbG90KEJlZXIpICsNCiAgbGFicyh4ID0gIkHDsW8iLCB5ID0gIk1lZ2FsaXRyb3MiLCANCiAgICAgICB0aXRsZSA9ICJQcm9kdWNjacOzbiBkZSBjZXJ2ZXphIHRyaW1lc3RyYWwgZW4gQXVzdHJhbGlhIikNCmBgYA0KDQpFeGlzdGVuIHZhcmlhcyBwcmVkaWN0b3JhcyBxdWUgcHVlZGVuIHNlciDDunRpbGVzIGVuIGVsIGFuw6FsaXNpcyBkZSByZWdyZXNpw7NuLg0KDQoxLiBQcmVkaWN0b3JhIGRlIHRlbmRlbmNpYSBgdHJlbmQoKWANCg0KJCQNCnlfdCA9IFxiZXRhXzAgKyBcYmV0YV8xdCArIFx2YXJlcHNpbG9uX3QNCiQkDQoNCjIuIFZhcmlhYmxlcyBkdW1teSBlc3RhY2lvbmFsZXMNCg0KTGFzIHZhcmlhYmxlcyBkdW1teSAodGFtYmnDqW4gbGxhbWFkYXMgZGljb3TDs21pY2FzLCBiaW5hcmlhcywgLi4uKSB0b21hbiBzb2xvIGRvcyB2YWxvcmVzOiAqKjEqKiBvICoqMCoqICh2ZXJkYWRlcm8vZmFsc28pLg0KDQpQYXJhIGFncmVnYXIgdmFyaWFibGVzIGR1bW15IGVzdGFjaW9uYWxlcywgYmFzdGEgY29uIGVzY3JpYmlyIGBzZWFzb24oKWAuDQoNCmBgYHtyfQ0KcmVjZW50X3Byb2R1Y3Rpb24gJT4lIA0KICBzZWxlY3QoUXVhcnRlcixCZWVyKSAlPiUgDQogIG11dGF0ZSh0ZW5kZW5jaWEgPSBzZXFfYWxvbmcocmVjZW50X3Byb2R1Y3Rpb24kUXVhcnRlciksDQogICAgICAgICBxMiA9IGlmX2Vsc2UocXVhcnRlcihRdWFydGVyKT09MiwxLDApLA0KICAgICAgICAgcTMgPSBpZl9lbHNlKHF1YXJ0ZXIoUXVhcnRlcik9PTMsMSwwKSwNCiAgICAgICAgIHE0ID0gaWZfZWxzZShxdWFydGVyKFF1YXJ0ZXIpPT00LDEsMCkNCiAgICAgICAgICkgJT4lIA0KICBtb2RlbChUU0xNKEJlZXIgfiB0ZW5kZW5jaWEgKyBxMiArIHEzICsgcTQpKSAlPiUgDQogIHJlcG9ydCgpDQpgYGANCg0KDQpgYGB7cn0NCmZpdF9iZWVyIDwtIHJlY2VudF9wcm9kdWN0aW9uICU+JSANCiAgbW9kZWwoVFNMTShCZWVyIH4gdHJlbmQoKSArIHNlYXNvbigpKSkNCg0KcmVwb3J0KGZpdF9iZWVyKQ0KYGBgDQoNCmBgYHtyfQ0KcCA8LSBhdWdtZW50KGZpdF9iZWVyKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IFF1YXJ0ZXIpKSArDQogIGdlb21fbGluZShhZXMoeSA9IEJlZXIsIGNvbG9yID0gIkRhdG9zIikpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gLmZpdHRlZCwgY29sb3IgPSAiRml0dGVkIikpICsNCiAgbGFicyh4ID0gIkHDsW8iLCB5ID0gIk1lZ2FsaXRyb3MiKQ0KDQpnZ3Bsb3RseShwKQ0KYGBgDQoNCkFsIHZlciBsYSBncsOhZmljYSwgdmVtb3MgcXVlIGV4aXN0ZSB1biB0cmltZXN0cmUgbXV5IHBvciBkZWJham8gZGVsIHJlc3RvLiBQb2RlbW9zIGFncmVnYXIgdW5hICoqdmFyaWFibGUgZGUgaW50ZXJ2ZW5jacOzbioqIHBhcmEgZXNlIHBlcmlvZG8uDQoNCiMjIyBTcGlrZSB2YXJpYWJsZXMNCg0KQ2FwdHVyYW4gZWwgZWZlY3RvIGRlIHVuIHNvbG8gcGVyaW9kby4NCg0KYGBge3J9DQpjZXJ2ZXphIDwtIHJlY2VudF9wcm9kdWN0aW9uICU+JSANCiAgc2VsZWN0KFF1YXJ0ZXIsIEJlZXIpICU+JSANCiAgbXV0YXRlKHEyXzk0ID0gaWZfZWxzZShRdWFydGVyID09IHllYXJxdWFydGVyKCIxOTk0IFEyIiksMSwwKSwNCiAgICAgICAgIHE0XzkyID0gaWZfZWxzZShRdWFydGVyID09IHllYXJxdWFydGVyKCIxOTkyIFE0IiksMSwwKSkNCmNlcnZlemENCmBgYA0KDQpBanVzdGFtb3MgdW4gbW9kZWxvLCBjb3JyaWdpZW5kbyBwb3IgZXNlIHBlcmlvZG8gb3V0bGllciAoMTk5NCBRMikuDQoNCmBgYHtyfQ0KZml0X2JlZXIgPC0gY2VydmV6YSAlPiUgDQogIG1vZGVsKFRTTE0oQmVlciB+IHRyZW5kKCkgKyBzZWFzb24oKSArIHEyXzk0KSkNCg0KcmVwb3J0KGZpdF9iZWVyKQ0KYGBgDQoNCmBgYHtyfQ0KcCA8LSBhdWdtZW50KGZpdF9iZWVyKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IFF1YXJ0ZXIpKSArDQogIGdlb21fbGluZShhZXMoeSA9IEJlZXIsIGNvbG9yID0gIkRhdG9zIikpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gLmZpdHRlZCwgY29sb3IgPSAiRml0dGVkIikpICsNCiAgbGFicyh4ID0gIkHDsW8iLCB5ID0gIk1lZ2FsaXRyb3MiKQ0KDQpnZ3Bsb3RseShwKQ0KYGBgDQoNCg0KIyMjIENhbWJpb3MgZGUgbml2ZWwNCg0KQ2FwdHVyYW4gZWwgZWZlY3RvIGEgcGFydGlyIGRlIGNpZXJ0byBwZXJpb2RvLg0KDQpgYGB7cn0NCmNlcnZlemEgPC0gY2VydmV6YSAlPiUgDQogIG11dGF0ZShkMjAwMCA9IGlmX2Vsc2UoeWVhcihRdWFydGVyKT49MjAwMCwxLDApKQ0KY2VydmV6YQ0KYGBgDQoNCmBgYHtyfQ0KZml0X2JlZXIgPC0gY2VydmV6YSAlPiUgDQogIG1vZGVsKFRTTE0oQmVlciB+IHRyZW5kKCkgKyBzZWFzb24oKSArIGQyMDAwKSkNCg0KcmVwb3J0KGZpdF9iZWVyKQ0KYGBgDQoNCmBgYHtyfQ0KcCA8LSBhdWdtZW50KGZpdF9iZWVyKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IFF1YXJ0ZXIpKSArDQogIGdlb21fbGluZShhZXMoeSA9IEJlZXIsIGNvbG9yID0gIkRhdG9zIikpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gLmZpdHRlZCwgY29sb3IgPSAiRml0dGVkIikpICsNCiAgbGFicyh4ID0gIkHDsW8iLCB5ID0gIk1lZ2FsaXRyb3MiKQ0KDQpnZ3Bsb3RseShwKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCmNlcnZlemEgJT4lIA0KICBnZ190c2Rpc3BsYXkoQmVlciAlPiUgZGlmZmVyZW5jZSg0KSwgcGxvdF90eXBlID0gInBhcnRpYWwiKQ0KYGBgDQoNCg0KDQoNCg0KDQpgYGB7cn0NCnRpYmJsZShtdWplciA9IGMoMSwwLDEsIDApLA0KICAgICAgIGhvbWJyZSA9IGMoMCwxLDAsIDApLA0KICAgICAgIG5vbWJyZSA9IGMoIkFuZHJlYSIsIkp1YW4iLCJTb2bDrWEiLCJGZXIiKSkNCmBgYA0KDQoqKkN1YW5kbyBzZSBjcmVhbiB2YXJhaWJsZXMgZHVtbXksIGxhIGNhbnRpZGFkIGRlIGR1bW1pZXMgYSBnZW5lcmFyIGVzIHVuYSBtZW5vcyBxdWUgZWwgdG90YWwgZGUgY2F0ZWdvcsOtYXMqKi4gU2kgc29uIGRvcyBjYXRlZ29yw61hcywgc29sbyBuZWNlc2l0YW1vcyB1bmEgZHVtbXkuIFNpIHNvbiB0cmVzLCBuZWNlc2l0YW1vcyAyLCBldGMuDQoNCg0KIyMgUmVncmVzaW9uZXMgbm8gbGluZWFsZXMNCg0KDQojIyMgTW9kZWxvcyBleHBvbmVuY2lhbGVzDQoNCiogTW9kZWxvcyBsb2ctbG9nDQoNCiQkDQpcbG9ne3lfdH0gPSBcYmV0YV8wICsgXGJldGFfMSBcbG9ne3hfdH0NCiQkDQoNCkxhIGludGVycHJldGFjacOzbiBkZSBsb3MgY29lZmljaWVudGVzICgkXGJldGFfcyQpIGVzIGNvbW8gZWxhc3RpY2lkYWRlcyAoY2FtYmlvcyBwb3JjZW50dWFsZXMpLg0KDQoNCiogTW9kZWxvcyBsaW4tbG9nDQoNCiQkDQp5X3QgPSBcYmV0YV8wICsgXGJldGFfMSBcbG9ne3hfdH0NCiQkDQoNCiogTW9kZWxvcyBsb2ctbGluDQoNCiQkDQpcbG9ne3lfdH0gPSBcYmV0YV8wICsgXGJldGFfMSB4X3QNCiQkDQoNCiMjIyBNb2RlbG9zIGRlIHJlZ3Jlc2nDs24gbGluZWFsIHBvciBwYXJ0ZXMgKCpwaWVjZXdpc2UqKQ0KDQpBcXXDrSBsYSByZWdyZXNpw7NuIHNlIGNhbGN1bGEgcG9yIHBhcnRlcyBlbiBlbCB0aWVtcG8sIHBhcmEgY2FwdHVyYXIgZGlzdGludGFzIHRlbmRlbmNpYXMuDQoNCmBgYHtyfQ0KYm9zdG9uX21lbiA8LSBib3N0b25fbWFyYXRob24gJT4lIA0KICBmaWx0ZXIoRXZlbnQgPT0gIk1lbidzIG9wZW4gZGl2aXNpb24iKSAlPiUgDQogIG11dGF0ZShNaW51dGVzID0gYXMubnVtZXJpYyhUaW1lKS82MCkNCg0KYm9zdG9uX21lbiAlPiUgDQogIGF1dG9wbG90KE1pbnV0ZXMpICsgDQogIGdndGl0bGUoIlRpZW1wb3MgZ2FuYWRvcmVzIGRlbCBtYXJhdMOzbiBkZSBCb3N0b24sIGNhdGVnb3LDrWEgYWJpZXJ0YSBkZSBob21icmVzIikNCmBgYA0KDQpWYW1vcyBhIG1vZGVsYXIgbG9zIHRpZW1wb3MgY29uIHVuYSByZWdyZXNpw7NuIHBvciBwYXJ0ZXMuDQoNCmBgYHtyfQ0KZml0X2Jvc3RvbiA8LSBib3N0b25fbWVuICU+JSANCiAgbW9kZWwoDQogICAgbGluZWFsID0gVFNMTShNaW51dGVzIH4gdHJlbmQoKSksDQogICAgZXhwb25lbmNpYWwgPSBUU0xNKGxvZyhNaW51dGVzKSB+IHRyZW5kKCkpLA0KICAgIGBSZWcuIHBvciBwYXJ0ZXNgID0gVFNMTShNaW51dGVzIH4gdHJlbmQoa25vdHMgPSBjKDE5NDAsMTk4MCkpKQ0KICApDQoNCmZjX2Jvc3RvbiA8LSBmaXRfYm9zdG9uICU+JSBmb3JlY2FzdChoID0gMTApDQoNCmJvc3Rvbl9tZW4gJT4lIA0KICBhdXRvcGxvdChNaW51dGVzKSArDQogIGdlb21fbGluZShhZXMoeSA9IC5maXR0ZWQsIGNvbG9yID0gLm1vZGVsKSwgZGF0YSA9IGZpdHRlZChmaXRfYm9zdG9uKSkgKw0KICBhdXRvbGF5ZXIoZmNfYm9zdG9uLCBhbHBoYSA9IDAuNSwgbGV2ZWwgPSA5NSkgKw0KICBnZ3RpdGxlKCJNYXJhdMOzbiBkZSBCb3N0b24sIGNhdC4gYWJpZXJ0YSBkZSBob21icmVzIikNCmBgYA0KDQo=